home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.mactech.com 2010
/
ftp.mactech.com.tar
/
ftp.mactech.com
/
machack
/
Hacks97
/
FinderDungeon.sit
/
Finder Dungeon
/
source code
/
DungeonE.c
< prev
next >
Wrap
Text File
|
1997-06-28
|
70KB
|
2,661 lines
/*DungeonE – prototype game example for the Mac Game book*/
/*By Ingemar Ragnemalm 1995*/
/**/
/*This game is somewhat similar to MemoryGame, in that it uses a grid, represented*/
/*by an array. The game is a typical (though extremely simplified) dungeon-digging game,*/
/*where the objective is to collect treasures and fight monsters.*/
/*Representation:*/
/*The array tileArr holds nearly all information we need. telling what is in each space in the grid.*/
/*Monsters and treasures are only represented this way. In a real game, you may wish to*/
/*keep a list of all monster and treasure positions, to avoid scanning for them and to keep more*/
/*information about each.*/
/*The game also keeps an array that tells what spaces are known to the player (tilesKnown), and*/
/*the player position (playerPosition), so we don't have to scan for it all the time.*/
/*The combat system is extremely simple. If you try moving to an enemy, you have 60% chance to*/
/*hit it and kill it. If an enemy tries to move to you, it has 50% chance to hit, reducing your hit points*/
/*by one.*/
/**/
/*The program ends when we select "Quit".*/
#include <Sound.h>
#include "MoreFilesExtras.h"
#include "Icons.h"
#include "ScriptableFinder.h"
#include "FullPath.h"
#include <Controls.h>
/*Size of the array*/
#define kArraySizeH 45
#define kArraySizeV 20
short kWindowSizeH = 24;
short kWindowSizeV = 16;
/*Size of the tiles*/
#define kTileSizeH 32
#define kTileSizeV 32
/*Number of extra pixels below game graphics*/
#define kScoreFieldHeight 15
/*Base line for text, how far from bottom of window*/
#define kScoreBaseLine 3
/*Menu ids*/
#define appleID 127
#define fileID 128
/* A macro for taking the abs of a value */
#define abs(x) (x>0?x:-x)
/* All the possible states of a tile (space in the dungeon) */
//typedef enum {empty, wall, player, enemy, tempEnemy, gold, exitPos} TileState;
typedef enum {empty, wall, fakeWall, exitPos, file, folder, app} TileState;
short levelVRefNum = -1;
long levelParId = 1;
/*The score is a global*/
long gGold, gExperience;
/* The window pointer */
WindowPtr myWindow = nil;
WindowPtr invWindow = nil;
/* Arrays describing the dungeon */
/* What tiles have we seen? */
Boolean tileKnown[kArraySizeH][kArraySizeV];
/* What does each tile contain? */
/* tileArray now only holds static objects */
TileState tileArray[kArraySizeH][kArraySizeV];
/*Variables describing the player:*/
//Point playerPosition; -> player->position
short playerHitPoints = 10; // NOT -> player->value
/* A boolean telling if we should quit yet or not */
Boolean gDone = false;
Rect gGameRect;
/*Pictures*/
PicHandle floorTile;
PicHandle wallTile;
PicHandle exitTile;
PicHandle playerTile;
PicHandle enemyTile;
PicHandle goldTile;
PicHandle fileTile;
PicHandle folderTile;
PicHandle appTile;
PicHandle deadPlayer;
/*All 8 directions as vectors*/
Point directionTable[8] = { { 0, 1 },
{-1, 1 },
{-1, 0 },
{-1,-1 },
{ 0,-1 },
{ 1,-1 },
{ 1, 0 },
{ 1, 1 }};
ControlHandle scrollH;
ControlHandle scrollV;
OSErr HFS_ReadCatIndex( // read an indexed catalog entry
SInt16 pVolume, // the volume
SInt32 pDirectory, // the directory
SInt32 pIndex, // the index
CInfoPBRec *rCatInfo, // returns the data
unsigned char *rName, // returns the name
Boolean *rEndOfCatalog); // returns end of catalog
OSErr ReadFSSpecIndex( // read an indexed FSSpec entry
SInt16 pVolume, // the volume
SInt32 pDirectory, // the directory
SInt32 pIndex, // the index
FSSpec *rFSSpec, // returns the FSSpec
Boolean *rEndOfCatalog); // returns end of catalog
pascal OSErr GetFDFlags(short vRefNum,
long dirID,
ConstStr255Param name,
Boolean *getBits,
unsigned short flagBits);
OSErr LaunchApplicationWithDocument(const FSSpec * applicationFSSpecPtr,
FSSpecArrayHandle fah);
OSErr FindAProcess(OSType typeToFind, OSType creatorToFind, ProcessSerialNumberPtr processSN,ProcessInfoRecPtr infoRecToFill);
static Boolean GetIndVRefNum(short which, short *vRefNum, StringPtr name);
pascal OSErr ResolveAliasFileMountOption(FSSpec *fileFSSpec,
Boolean resolveAliasChains,
Boolean *targetIsFolder,
Boolean *wasAliased,
Boolean mountRemoteVols);
void PlaySoundAsync(StringPtr soundName);
static OSErr FSpGetIcon(FSSpec *spec, Handle *iconSuite);
static void SendODOCToProcess(ProcessSerialNumberPtr psn, FSSpecArrayHandle fah);
static void DoUpdate();
/************************************************/
/* Types and variables for game entity handling */
/************************************************/
/* All the possible kinds of game entities */
typedef enum {playerEntity, enemyEntity, goldEntity, fileEntity, directoryEntity, applicationEntity} EntityType;
/*Variables describing a game entity*/
typedef struct GameEntityRecord {
EntityType kind; /* What kind of entity? */
Point position; /* Where in the grid? */
short value; /* Hit points or point value */
FSSpec spec; /* name of file or directory */
OSType fileType;
Handle iconSuite; /* icon Suite */
struct GameEntityRecord *standingOnEntityItem; /* what item is this entity on top of at the moment? */
struct GameEntityRecord *previous, *next; /* Next and previous entity in the list */
} GameEntityRecord;
typedef GameEntityRecord *GameEntityPtr; /* Define a handle type to it */
/* A global pointer is the root of the entity list */
GameEntityPtr gEntityList = nil;
/* The player entity must be easily accessible */
GameEntityPtr player;
/* tileArray now only holds static objects */
/* What entity is in each tile? */
GameEntityPtr entityArray[kArraySizeH][kArraySizeV];
GameEntityPtr playerEntityItem = nil;
/*** End of game entity handling types and variables ***/
typedef struct HeldItemRecord {
EntityType kind; /* What kind of entity? */
FSSpec spec;
Handle iconSuite;
struct HeldItemRecord *previous, *next; /* Next and previous item in the list */
} HeldItemRecord;
typedef HeldItemRecord *HeldItemPtr; /* Define a handle type to it */
HeldItemPtr heldItemsList = nil;
static void ClipToGameArea()
{
Rect clip;
SetPort(myWindow);
clip = myWindow->portRect;
clip.right -= 15;
clip.bottom -= 15+ kScoreFieldHeight;
ClipRect(&clip);
}
static void UnclipToGameArea()
{
Rect clip = {-32000, -32000, 32000, 32000};
SetPort(myWindow);
ClipRect(&clip);
}
#define kScrollBuffer 5
static void ScrollIntoView(Point pt)
{
short vScroll, hScroll;
short hDelta = 0, vDelta = 0;
vScroll = GetControlValue(scrollV);
hScroll = GetControlValue(scrollH);
if( pt.h < hScroll)
{
hDelta = -(hScroll - pt.h + kScrollBuffer);
}
if( pt.h > hScroll + kWindowSizeH -1)
{
hDelta = (pt.h - (hScroll + kWindowSizeH)) + kScrollBuffer;
}
if( pt.v < vScroll)
{
vDelta = -(vScroll - (pt.v + kScrollBuffer));
}
if( pt.v > vScroll + kWindowSizeV - 1)
{
vDelta = (pt.v - (vScroll + kWindowSizeV)) + kScrollBuffer;
}
if(hDelta || vDelta)
{
SetControlValue(scrollV, vScroll + vDelta);
SetControlValue(scrollH, hScroll + hDelta);
InvalRect(&myWindow->portRect);
DoUpdate();
}
}
/* A function that generates a value in the interval 0..range-1 */
static short Rand(short range)
{
return (Random () & 0x7fff) % range;
}; /*Rand*/
/* Draw a tile */
static void DrawTile(short h, short v)
{
Rect tileRectangle, boardRect;
short scV, scH;
SetRect(&tileRectangle, h * kTileSizeH, v * kTileSizeV, (h + 1) * kTileSizeH, (v + 1) * kTileSizeV);
boardRect = gGameRect;
boardRect.bottom -= kScoreFieldHeight;
scV = GetControlValue(scrollV);
scH = GetControlValue(scrollH);
SetOrigin(scH * kTileSizeH, scV * kTileSizeV);
ClipToGameArea();
if(!RectInRgn(&tileRectangle, myWindow->visRgn))
{
SetOrigin(0,0);
return;
}
if ( tileKnown[h][v] )
{
switch ( tileArray[h][v] )
{
case empty:
DrawPicture(floorTile, &tileRectangle);
break;
case wall:
DrawPicture(wallTile, &tileRectangle);
break;
case fakeWall:
DrawPicture(wallTile, &tileRectangle);
break;
case exitPos:
DrawPicture(exitTile, &tileRectangle);
break;
case app:
if(entityArray[h][v] && entityArray[h][v]->iconSuite)
{
DrawPicture(floorTile, &tileRectangle);
PlotIconSuite(&tileRectangle,atVerticalCenter | atHorizontalCenter,ttNone,entityArray[h][v]->iconSuite);
}
else
DrawPicture(appTile, &tileRectangle);
break;
case folder:
if(entityArray[h][v] && entityArray[h][v]->iconSuite)
{
DrawPicture(floorTile, &tileRectangle);
PlotIconSuite(&tileRectangle,atVerticalCenter | atHorizontalCenter,ttNone,entityArray[h][v]->iconSuite);
}
else
DrawPicture(folderTile, &tileRectangle);
break;
case file:
if(entityArray[h][v] && entityArray[h][v]->iconSuite)
{
DrawPicture(floorTile, &tileRectangle);
PlotIconSuite(&tileRectangle,atVerticalCenter | atHorizontalCenter,ttNone,entityArray[h][v]->iconSuite);
}
else
DrawPicture(fileTile, &tileRectangle);
break;
default:
PaintRect(&tileRectangle);
}
if (entityArray[h][v] != nil)
switch (entityArray[h][v]->kind)
{
case playerEntity:
if(playerHitPoints > 0)
DrawPicture(playerTile, &tileRectangle);
else
DrawPicture(deadPlayer, &tileRectangle);
break;
case enemyEntity:
DrawPicture(enemyTile, &tileRectangle);
break;
case goldEntity:
DrawPicture(goldTile, &tileRectangle);
break;
case applicationEntity:
if(entityArray[h][v]->iconSuite)
{
DrawPicture(floorTile, &tileRectangle);
PlotIconSuite(&tileRectangle,atVerticalCenter | atHorizontalCenter,ttNone,entityArray[h][v]->iconSuite);
}
else
DrawPicture(appTile, &tileRectangle);
break;
case directoryEntity:
if(entityArray[h][v]->iconSuite)
{
DrawPicture(floorTile, &tileRectangle);
PlotIconSuite(&tileRectangle,atVerticalCenter | atHorizontalCenter,ttNone,entityArray[h][v]->iconSuite);
}
else
DrawPicture(folderTile, &tileRectangle);
break;
case fileEntity:
if(entityArray[h][v]->iconSuite)
{
DrawPicture(floorTile, &tileRectangle);
PlotIconSuite(&tileRectangle,atVerticalCenter | atHorizontalCenter,ttNone,entityArray[h][v]->iconSuite);
}
else
DrawPicture(fileTile, &tileRectangle);
break;
default:
;
}
}
else
PaintRect(&tileRectangle);
UnclipToGameArea();
SetOrigin(0,0);
} /*DrawTile*/
static void ShowAll(void)
{
short i, j;
for(i = 0; i < kArraySizeH; i++)
for(j = 0; j < kArraySizeV; j++)
{
tileKnown[i][j] = true;
}
InvalRect(&gGameRect);
DoUpdate();
}
static void InvalInventory()
{
GrafPtr savePort;
GetPort(&savePort);
SetPort(invWindow);
InvalRect(&invWindow->portRect);
SetPort(savePort);
}
/* NewEntity allocates space for a new entity and puts it in the entity list */
static HeldItemPtr NewHeldItem()
{
HeldItemPtr newEntity;
newEntity = (HeldItemPtr) NewPtr(sizeof(HeldItemRecord));
if (newEntity == nil) return nil;
if (heldItemsList != nil)
{
heldItemsList->previous = newEntity;
}
newEntity->next = heldItemsList;
newEntity->previous = nil;
heldItemsList = newEntity;
InvalInventory();
return newEntity;
} /*NewEntity*/
/* MakeEntity builds an entity with the given values and puts it into the entityArray */
static HeldItemPtr PickUpItem(GameEntityPtr item)
{
HeldItemPtr newEntity;
if(item->kind == fileEntity ||item->kind == directoryEntity ||item->kind == applicationEntity)
{
newEntity = NewHeldItem();
if (newEntity == nil) return nil;
newEntity->kind = item->kind;
newEntity->iconSuite = nil;
FSpGetIcon(&item->spec, &newEntity->iconSuite);
newEntity->spec = item->spec;
}
return newEntity;
} /*PickUpItem*/
static void OpenItem(GameEntityPtr item)
{
if(item)
{
FSSpecArrayHandle fah = (FSSpecArrayHandle)NewHandle(0);
AddToFSSpecArrayHandle(&item->spec, fah);
OpenScriptableFinderSelection (fah, false, kAENoReply, nil);
DisposeHandle((Handle)fah);
if(item->kind == directoryEntity)
{
ProcessSerialNumber processSN;
ProcessInfoRecPtr info;
if(FindAProcess('FNDR', 'MACS', &processSN,nil) == noErr)
{
SetFrontProcess(&processSN);
}
}
}
}
/* DisposeEntity removes an entity from the list and disposes it. */
static void DisposeHeldItem(HeldItemPtr doomedEntity)
{
if (doomedEntity == nil) return;
if (doomedEntity->next != nil)
doomedEntity->next->previous = doomedEntity->previous;
if (doomedEntity->previous != nil)
doomedEntity->previous->next = doomedEntity->next;
if (doomedEntity == heldItemsList)
heldItemsList = doomedEntity->next;
if(doomedEntity->iconSuite)
DisposeIconSuite(doomedEntity->iconSuite, true);
DisposePtr((Ptr)doomedEntity);
InvalInventory();
} /*DisposeEntity*/
/* drop held items on the current item */
static void DropItems(GameEntityPtr item)
{
HeldItemPtr newEntity;
if( item == nil)
{
FSSpecArrayHandle fah = (FSSpecArrayHandle)NewHandle(0);
HeldItemPtr items;
for(items = heldItemsList; items; items = items->next)
{
AddToFSSpecArrayHandle(&items->spec, fah);
}
OpenScriptableFinderSelection (fah, false, kAENoReply, nil);
DisposeHandle((Handle)fah);
return;
}
if( item->kind == applicationEntity && heldItemsList)
{
FSSpecArrayHandle fah = (FSSpecArrayHandle)NewHandle(0);
HeldItemPtr items;
FSSpec appSpec;
ProcessSerialNumber psn;
FInfo fndrInfo;
for(items = heldItemsList; items; items = items->next)
{
AddToFSSpecArrayHandle(&items->spec, fah);
}
appSpec = item->spec;
FSpGetFInfo(&appSpec, &fndrInfo);
if(FindAProcess(fndrInfo.fdType, fndrInfo.fdCreator, &psn,nil) == noErr)
SendODOCToProcess(&psn, fah);
else
LaunchApplicationWithDocument(&appSpec,fah);
DisposeHandle((Handle)fah);
while (heldItemsList != nil) DisposeHeldItem(heldItemsList);
}
if(item->kind == directoryEntity)
{
}
} /*DropItems*/
/*************************************/
/* Routines for game entity handling */
/*************************************/
/* NewEntity allocates space for a new entity and puts it in the entity list */
static GameEntityPtr NewEntity()
{
GameEntityPtr newEntity;
newEntity = (GameEntityPtr) NewPtr(sizeof(GameEntityRecord));
if (newEntity == nil) return nil;
if (gEntityList != nil)
{
gEntityList->previous = newEntity;
}
newEntity->next = gEntityList;
newEntity->previous = nil;
newEntity->standingOnEntityItem = nil;
gEntityList = newEntity;
return newEntity;
} /*NewEntity*/
/* DisposeEntity removes an entity from the list and disposes it. */
static void DisposeEntity(GameEntityPtr doomedEntity)
{
if (doomedEntity == nil) return;
if (doomedEntity->next != nil)
doomedEntity->next->previous = doomedEntity->previous;
if (doomedEntity->previous != nil)
doomedEntity->previous->next = doomedEntity->next;
if (doomedEntity == gEntityList)
gEntityList = doomedEntity->next;
if(doomedEntity->iconSuite)
DisposeIconSuite(doomedEntity->iconSuite, true);
DisposePtr((Ptr)doomedEntity);
} /*DisposeEntity*/
enum {
GenericFloppy,
GenericServer,
GenericCDRom,
GenericHD,
GenericRAMDisk,
IomegaBernoulliDisk
};
static short GetFilesVolumeType(short vRefNum)
{
DCtlHandle theDCtl;
StringPtr theDriverName;
short theType;
OSErr theError;
HParamBlockRec pb;
pb.volumeParam.ioVolIndex = 0; /* use vRefNum */
pb.volumeParam.ioVRefNum = vRefNum;
pb.volumeParam.ioNamePtr = nil; /* don't care about name */
theError = PBHGetVInfoSync(&pb);
if (theError == noErr) {
theDCtl = GetDCtlEntry(pb.volumeParam.ioVDRefNum);
if (((**theDCtl).dCtlFlags & (1L << 6)) == 1) { /* RAM or ROM */
/* RAM => dctlDriver is handle */
theDriverName = (StringPtr)*((Handle)(**theDCtl).dCtlDriver);
} else {
/* ROM => dctlDriver is pointer */
theDriverName = (StringPtr)(**theDCtl).dCtlDriver;
}
theDriverName += 18;
if (EqualString(theDriverName, "\p.Sony", true, true)) {
theType = GenericFloppy;
} else if (EqualString(theDriverName, "\p.AFPTranslator", true,
true)) {
theType = GenericServer;
} else if (EqualString(theDriverName, "\p.AppleCD", true,
true)) {
theType = GenericCDRom;
} else if (EqualString(theDriverName, "\p.EDisk", true,
true)) {
theType = GenericRAMDisk;
} else if (EqualString(theDriverName, "\p.SC/IOMEGA3.4", true,
true)) {
theType = IomegaBernoulliDisk;
} else {
theType = GenericHD;
}
} else {
theType = GenericHD;
}
return theType;
}
static Boolean _GetIconSuiteForTypeCreator(OSType inCreator, OSType inType, short vRefNum, Handle *outIconSuite)
{
DTPBRec dtpb;
short dtdbRefNum = 0, i;
OSErr err;
short resRef, saveResRefNum = CurResFile();
short count = 0, numMenuItems;
Str63 appName;
Handle iconData;
short volumeType;
if(vRefNum)
{
dtpb.ioNamePtr = NULL;
dtpb.ioVRefNum = vRefNum;
err = PBDTGetPath(&dtpb);
}
else
{
dtpb.ioResult = noErr;
}
if(dtpb.ioResult == noErr)
{
dtdbRefNum = dtpb.ioDTRefNum;
volumeType = GetFilesVolumeType(vRefNum);
if(volumeType != GenericServer && volumeType != GenericFloppy )
{
dtpb.ioCompletion = nil;
dtpb.ioDTRefNum = dtdbRefNum;
dtpb.ioNamePtr = appName;
dtpb.ioIndex = 0;
dtpb.ioFileCreator = inCreator;
// err = PBDTGetAPPL( &dtpb, FALSE);
if(err == noErr)
{
dtpb.ioFileType = inType;
dtpb.ioTagInfo = 0;
err = NewIconSuite( outIconSuite );
if(!err)
{
iconData = NewHandle(1024); //space for icl8
if(iconData)
{
HLock(iconData);
dtpb.ioDTBuffer = *iconData;
dtpb.ioDTReqCount = 1024;
dtpb.ioIconType = kLarge8BitIcon;
err = PBDTGetIconSync(&dtpb);
if(err == noErr)
{
err = AddIconToSuite(iconData,*outIconSuite,'icl8');
HUnlock(iconData);
}
else
{
DisposeHandle(iconData);
}
}
iconData = NewHandle(512); //space for icl4
if(iconData)
{
HLock(iconData);
dtpb.ioDTBuffer = *iconData;
dtpb.ioDTReqCount = 512;
dtpb.ioIconType = kLarge4BitIcon;
err = PBDTGetIconSync(&dtpb);
if(err == noErr)
{
err = AddIconToSuite(iconData,*outIconSuite,'icl4');
HUnlock(iconData);
}
else
{
DisposeHandle(iconData);
}
}
iconData = NewHandle(256);
HLock(iconData);
if(iconData)
{
dtpb.ioDTBuffer = *iconData;
dtpb.ioDTReqCount = 256;
dtpb.ioIconType = kLargeIcon;
err = PBDTGetIconSync(&dtpb);
if(err == noErr)
{
err = AddIconToSuite(iconData,*outIconSuite,'ICN#');
HUnlock(iconData);
return true;
}
else
{
//if we can't get the 'ICN#', we don't have a mask and can't draw!
DisposeHandle(iconData);
DisposeIconSuite(*outIconSuite, true);
*outIconSuite = nil;
}
}
}
}
}
}
return false;
}
static Boolean FSpGetHasCustomIcon(const FSSpec *spec)
{
Boolean result = false;
OSErr err = GetFDFlags(spec->vRefNum, spec->parID, spec->name, &result, kHasCustomIcon);
return result;
}
static OSErr FSpGetIcon(FSSpec *spec, Handle *iconSuite)
{
OSErr err;
CInfoPBRec pb;
Str31 tempName;
OSErr error;
short realVRefNum, vRefNum;
long parID, dirID;
StringPtr name;
name = spec->name;
vRefNum = spec->vRefNum;
dirID = spec->parID;
/* Protection against File Sharing problem */
if ( (name == NULL) || (name[0] == 0) )
{
tempName[0] = 0;
pb.hFileInfo.ioNamePtr = tempName;
pb.hFileInfo.ioFDirIndex = -1; /* use ioDirID */
}
else
{
pb.hFileInfo.ioNamePtr = (StringPtr)name;
pb.hFileInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */
}
pb.hFileInfo.ioVRefNum = vRefNum;
pb.hFileInfo.ioDirID = dirID;
error = PBGetCatInfoSync(&pb);
if ( error == noErr )
{
if(pb.hFileInfo.ioFlFndrInfo.fdFlags & kHasCustomIcon || pb.hFileInfo.ioFlFndrInfo.fdFlags & kIsAlias || spec->parID == 1)
{
err = GetScriptableFinderFileIcon (spec, false, kAEWaitReply, iconSuite);
}
else
{
//if its a directory, we will just use the generic directory icon
if( (pb.hFileInfo.ioFlAttrib & ioDirMask) == 0 ) //its not directory
{
if(_GetIconSuiteForTypeCreator(pb.hFileInfo.ioFlFndrInfo.fdCreator, pb.hFileInfo.ioFlFndrInfo.fdType, vRefNum, iconSuite) == noErr)
{
//go with the generic
*iconSuite = nil;
}
}
}
}
return err;
}
/* MakeEntity builds an entity with the given values and puts it into the entityArray */
static GameEntityPtr MakeEntity(EntityType eType, short hPos, short vPos, short value, FSSpec *spec, OSType fileType)
{
GameEntityPtr newEntity;
newEntity = NewEntity();
if (newEntity == nil) return nil;
newEntity->position.h = hPos;
newEntity->position.v = vPos;
newEntity->kind = eType;
newEntity->value = value;
newEntity->iconSuite = nil;
newEntity->fileType = fileType;
if(spec)
{
Handle iconHandle;
FInfo fndrInfo;
OSErr err;
newEntity->spec = *spec;
err = FSpGetIcon (&newEntity->spec, &newEntity->iconSuite);
}
else
newEntity->spec.name[0] = 0;
/* Border checking here would be good for safety */
entityArray[newEntity->position.h][newEntity->position.v] = newEntity;
// DrawTile(newEntity->position.h, newEntity->position.v);
return newEntity;
} /*MakeEntity*/
/* KillEntity disposes an entity, removes it from EntityArray and erases it */
static void KillEntity(GameEntityPtr doomedEntity)
{
if (doomedEntity == nil) return;
if (entityArray[doomedEntity->position.h][doomedEntity->position.v] == doomedEntity)
entityArray[doomedEntity->position.h][doomedEntity->position.v] = nil;
DrawTile(doomedEntity->position.h, doomedEntity->position.v);
DisposeEntity(doomedEntity);
} /*KillEntity*/
/*** End of game entity handling routines ***/
/*** Score handling and display ***/
static void DrawScore()
{
Str255 tempString;
Rect blankRect;
SetPort(myWindow);
blankRect = gGameRect;
blankRect.top = gGameRect.bottom - kScoreFieldHeight;
EraseRect(&blankRect);
MoveTo(gGameRect.right * 1 / 5, gGameRect.bottom - kScoreBaseLine);
DrawString("\pGold: ");
NumToString(gGold, tempString);
DrawString(tempString);
MoveTo(gGameRect.right * 2 / 5, gGameRect.bottom - kScoreBaseLine);
DrawString("\pExperience: ");
NumToString(gExperience, tempString);
DrawString(tempString);
MoveTo(gGameRect.right * 3 / 5, gGameRect.bottom - kScoreBaseLine);
DrawString("\pHit points: ");
NumToString(playerHitPoints, tempString);
DrawString(tempString);
if(player->standingOnEntityItem)
{
TextSize(9);
TextFace(bold);
MoveTo(gGameRect.left + 2, gGameRect.bottom - kScoreBaseLine);
DrawString(player->standingOnEntityItem->spec.name);
}
TextSize(12);
TextFace(0);
} /*DrawScore*/
static void AddScore(long addToGold, long addToExperience)
{
gGold += addToGold;
gExperience += addToExperience;
DrawScore();
} /*AddScore*/
#define kNumHealthTypes 7
OSType healthTypes[kNumHealthTypes] = {'MSIE', 'XCEL', 'PPT3','MSWD', 'PWSC', 'WFOS', 'MSNM'};
static void PayForHealth(void)
{
short i;
if(player->standingOnEntityItem)
{
for(i = 0; i < kNumHealthTypes; i++)
{
if(player->standingOnEntityItem->fileType == healthTypes[i])
{
if(gGold > 100)
{
gGold -= 100;
playerHitPoints++;
DrawScore();
PlaySoundAsync("\pBought Health");
}
}
}
}
}
/* Set all tiles around the player to be known, and draw them if they were not known before. */
static void ShowAroundPlayer()
{
short h, v;
ScrollIntoView(player->position);
for ( h = player->position.h - 1 ; h <= player->position.h + 1 ; h++)
for ( v = player->position.v - 1 ; v <= player->position.v + 1 ; v++)
if ( ! tileKnown[h][v] )
{
tileKnown[h][v] = true;
DrawTile(h, v);
}
} /*ShowAroundPlayer*/
static Boolean FindRandomEmptySpot(Point *freeSpot, Rect *rooms, short numRooms)
{
short i;
short room;
//lets try real randomness for a little while
for(i = 1; i <= 50; i++)
{
freeSpot->v = Rand(kArraySizeV);
freeSpot->h = Rand(kArraySizeH);
if(entityArray[freeSpot->h][freeSpot->v] == nil && tileArray[freeSpot->h][freeSpot->v] == empty)
return true;
}
//if we got here, we still haven't found an empty spot
//Choose a random room, then look for a spot in it.
for(i = 1; i <= 50; i++)
{
room = Rand(numRooms);
freeSpot->v = rooms[room].top + Rand(rooms[room].bottom - rooms[room].top);
freeSpot->h = rooms[room].left + Rand(rooms[room].right - rooms[room].left);
if(entityArray[freeSpot->h][freeSpot->v] == nil && tileArray[freeSpot->h][freeSpot->v] == empty)
return true;
}
//finally, walk the room list and find the first empty spot
for(room = 0; room < numRooms; room++)
{
for(freeSpot->v = rooms[room].top; freeSpot->v <= rooms[room].bottom; freeSpot->v ++)
{
for(freeSpot->h = rooms[room].left; freeSpot->h <= rooms[room].right; freeSpot->h ++)
{
if(entityArray[freeSpot->h][freeSpot->v] == nil && tileArray[freeSpot->h][freeSpot->v] == empty)
return true;
}
}
}
return false;
}
/* The level generation routine */
static void CreateLevel()
{
Rect roomRect[20];
short room;
short height, width;
short h, v, h1, v1, h2, v2;
short numRooms;
short numTreasures, numMonsters;
short i;
short fileIndex;
FSSpec fileSpec;
long freeSpots = 0;
short pathLen;
Handle path;
Str255 title;
Boolean done = false;
/*For each tile position, we set the initial state.*/
/**/
/*Here we generate the dungeon randomly, with a rather simple algorithm.*/
/*Most real games use pre-designed levels, preferrably stored in resources.*/
/*Dungeon generation algorithm:*/
/*We select a number of rooms to be placed, 3-10*/
/*Each room is randomly assigned a size, and then a position.*/
/*In each room we may put monsters and/or treasures.*/
/*From each room except the last, we make a path from the room to the next.*/
/*This guarantees that all rooms are connected.*/
SetControlValue(scrollV, 0);
SetControlValue(scrollH, 0);
if(GetFullPath(levelVRefNum,levelParId,nil,&pathLen,&path) == noErr)
{
title[0] = (pathLen > 250 ? 250 : pathLen);
BlockMoveData(*path, &(title[1]), pathLen);
SetWTitle(myWindow, title);
}
else
SetWTitle(myWindow, "\pDrives");
/*First fill the entire dungeon with walls!*/
for ( h = 0 ; h < kArraySizeH ; h++)
for ( v = 0 ; v < kArraySizeV ; v++)
tileArray[h][v] = wall;
/*All tiles are unknown when we start*/
/*This must be in the beginning now, since MakeEntity draws*/
/*the new entity*/
for ( h = 0 ; h < kArraySizeH ; h++)
for ( v = 0 ; v < kArraySizeV ; v++)
tileKnown[h][v] = false;
/*Dispose all old entities*/
while (gEntityList != nil) DisposeEntity(gEntityList);
/*Fill the entityArray with nil - there's nobody there!*/
for ( h = 0 ; h < kArraySizeH ; h++)
for ( v = 0 ; v < kArraySizeV ; v++)
entityArray[h][v] = nil;
numRooms = 5 + Rand(5); /*5 to 9 rooms*/
/*Create each room*/
for ( room = 0 ; room <= numRooms - 1 ; room++)
{
height = 1 + Rand(5 - numRooms / 2);
width = 1 + Rand(5 - numRooms / 2);
roomRect[room].top = 1 + Rand(kArraySizeV - height - 2);
roomRect[room].bottom = roomRect[room].top + height;
roomRect[room].left = 1 + Rand(kArraySizeH - width - 2);
roomRect[room].right = roomRect[room].left + width;
for ( h = roomRect[room].left ; h <= roomRect[room].right ; h++)
for ( v = roomRect[room].top ; v <= roomRect[room].bottom ; v++)
{
tileArray[h][v] = empty;
freeSpots++;
}
};
/*Make paths between all rooms*/
for ( room = 0 ; room <= numRooms - 2 ; room++)
{
/*We make a path from h1, h2, to h2, v2*/
h1 = roomRect[room].left + Rand(roomRect[room].right - roomRect[room].left + 1);
v1 = roomRect[room].top + Rand(roomRect[room].bottom - roomRect[room].top + 1);
h2 = roomRect[room + 1].left + Rand(roomRect[room + 1].right - roomRect[room + 1].left + 1);
v2 = roomRect[room + 1].top + Rand(roomRect[room + 1].bottom - roomRect[room + 1].top + 1);
/*First move along the h axis*/
if ( h1 < h2 )
for ( h = h1 ; h <= h2 ; h++)
{
if(Rand(10) == 0)
{
tileArray[h][v1] = fakeWall;
}
else
{
tileArray[h][v1] = empty;
}
freeSpots++;
}
else
for ( h = h1 ; h >= h2 ; h--)
{
tileArray[h][v1] = empty;
freeSpots++;
}
/*And then along the v axis*/
if ( v1 < v2 )
for ( v = v1 ; v <= v2 ; v++)
{
if(Rand(10) == 0)
{
tileArray[h2][v] = fakeWall;
}
else
{
tileArray[h2][v] = empty;
}
freeSpots++;
}
else
for ( v = v1 ; v >= v2 ; v--)
{
tileArray[h2][v] = empty;
freeSpots++;
}
};
/*Now populate the rooms!*/
for ( room = 1 ; room <= numRooms - 1 ; room++)
{
numTreasures = Rand(3); /*0 to 2 treasures*/
for ( i = 1 ; i <= numTreasures ; i++)
{
h = roomRect[room].left + Rand(roomRect[room].right - roomRect[room].left + 1);
v = roomRect[room].top + Rand(roomRect[room].bottom - roomRect[room].top + 1);
if (entityArray[h][v] == nil)
MakeEntity(goldEntity, h, v, Rand(100) + 1, nil, 'gold');
// tileArray[h][v] = gold;
};
if(Rand(2))
numMonsters = Rand(2); /*0 to 1 monsters*/
else
numMonsters = 0;
for ( i = 1 ; i <= numMonsters; i++)
{
h = roomRect[room].left + Rand(roomRect[room].right - roomRect[room].left + 1);
v = roomRect[room].top + Rand(roomRect[room].bottom - roomRect[room].top + 1);
if (entityArray[h][v] == nil)
MakeEntity(enemyEntity, h, v, 2, nil, 'bomb');
// tileArray[h][v] = enemy;
};
};
/*Finally, place the player in the first room and the exit in the last. Also check that the exit is*/
/*not the same position as the player! (It can happen, since we don't mak sure that rooms don't*/
/*overlap!)*/
/*Player position:*/
h = roomRect[0].left + Rand(roomRect[0].right - roomRect[0].left + 1);
v = roomRect[0].top + Rand(roomRect[0].bottom - roomRect[0].top + 1);
if (entityArray[h][v] != nil)
KillEntity(entityArray[h][v]);
player = MakeEntity(playerEntity, h, v, 5, nil, 'plyr');
// tileArray[h][v] = player;
/*Exit position:*/
h = roomRect[numRooms - 1].left + Rand(roomRect[numRooms - 1].right - roomRect[numRooms - 1].left + 1);
v = roomRect[numRooms - 1].top + Rand(roomRect[numRooms - 1].bottom - roomRect[numRooms - 1].top + 1);
/*But please don't overwrite the player with the exit!*/
if ( h == player->position.h )
if ( v == player->position.v )
{
/*Try another room:*/
h = roomRect[numRooms - 2].left + Rand(roomRect[numRooms - 2].right - roomRect[numRooms - 2].left + 1);
v = roomRect[numRooms - 2].top + Rand(roomRect[numRooms - 2].bottom - roomRect[numRooms - 2].top + 1);
/*Still failure? Darn. Just take a space next to the player.*/
if ( h == player->position.h )
if ( v == player->position.v )
if ( h < kArraySizeH )
h = h + 1;
else
h = h - 1;
};
/*OK, that's enough. Set the exit!*/
tileArray[h][v] = exitPos;
//Place the files
for(fileIndex = 1; !done; fileIndex ++)
{
OSErr err = noErr;
Point freeSpot;
if(levelParId == 1)
{
fileSpec.parID = 1;
done = !GetIndVRefNum(fileIndex, &fileSpec.vRefNum, fileSpec.name);
}
else
err = ReadFSSpecIndex(levelVRefNum,levelParId,fileIndex,&fileSpec, &done);
if(err) done = true;
if(!done)
{
if(FindRandomEmptySpot(&freeSpot, roomRect, numRooms))
{
long newDirId;
Boolean isDirectory;
FInfo fndrInfo;
Boolean wasAlias;
FSpGetFInfo(&fileSpec, &fndrInfo);
if(fndrInfo.fdFlags & kIsAlias)
{
err = ResolveAliasFileMountOption(&fileSpec,true,&isDirectory,&wasAlias, false);
}
err = FSpGetDirectoryID(&fileSpec, &newDirId, &isDirectory);
if(isDirectory)
{
MakeEntity(directoryEntity, freeSpot.h, freeSpot.v, 0, &fileSpec, fndrInfo.fdCreator);
tileArray[freeSpot.h][freeSpot.v] = folder;
}
else
if(fndrInfo.fdType == 'APPL')
{
MakeEntity(applicationEntity, freeSpot.h, freeSpot.v, 0, &fileSpec, fndrInfo.fdCreator);
tileArray[freeSpot.h][freeSpot.v] = app;
}
else
{
MakeEntity(fileEntity, freeSpot.h, freeSpot.v, 0, &fileSpec, fndrInfo.fdCreator);
tileArray[freeSpot.h][freeSpot.v] = file;
}
}
}
}
/*All tiles are unknown when we start*/
/*…except the ones around the player*/
/*Make the spaces around the player known*/
ShowAroundPlayer();
/*Draw all tiles!*/
for ( h = 0 ; h < kArraySizeH ; h++)
for ( v = 0 ; v < kArraySizeV ; v++)
DrawTile(h, v);
} /*CreateLevel*/
/* Move an enemy */
static void MoveEnemy(GameEntityPtr theEnemy /*short h, short v*/)
{
short dist;
short newh, newv;
short dir;
/*1: decide if we are close to enough to the player to "hear" the player*/
dist = abs(theEnemy->position.h - player->position.h) + abs(theEnemy->position.v - player->position.v); /*City Block distance*/
/*2: Make a suggested destination, newh, newv*/
if ( dist < Rand(15) ) /*Move towards the player*/
{
if ( theEnemy->position.h < player->position.h )
newh = theEnemy->position.h + 1;
else if ( theEnemy->position.h > player->position.h )
newh = theEnemy->position.h - 1;
else
newh = theEnemy->position.h;
if ( theEnemy->position.v < player->position.v )
newv = theEnemy->position.v + 1;
else if ( theEnemy->position.v > player->position.v )
newv = theEnemy->position.v - 1;
else
newv = theEnemy->position.v;
}
else /*Move randomly*/
{
dir = Rand(8);
newh = theEnemy->position.h + directionTable[dir].h;
newv = theEnemy->position.v + directionTable[dir].v;
}
/*3: Check what is in the destination and take appropriate action (move, fight)*/
switch ( tileArray[newh][newv] )
{
case empty:
case exitPos:
case app:
case file:
case folder:
case fakeWall:
/* tileArray[newh][newv] = tempEnemy; /*We can't use "enemy", since then we might process it again in the same move!*/
/* tileArray[h][v] = empty;*/
if (entityArray[newh][newv] == player)
{
if ( Rand(10) > 5 ) /*Does it hit?*/
{ /*Enemy hits player!*/
playerHitPoints--; /*Reduce player hit points*/
DrawScore(); /*Draw score to display the lower hit points!*/
if ( playerHitPoints > 0 )
{ /*Player still lives!*/
if ( noErr != SndPlay(nil, (SndListHandle)GetNamedResource('snd ', "\pPlayer hit"), false) )
;
}
else
{ /*Player died!*/
DrawTile(player->position.h, player->position.v);
PlaySoundAsync("\pPlayer died");
};
}
else
{ /*Miss!*/
if ( noErr != SndPlay(nil, (SndListHandle)GetNamedResource('snd ', "\pPlayer miss"), false) )
;
}
}
else
if (entityArray[newh][newv] == nil)
{
if(theEnemy->standingOnEntityItem)
{
entityArray[theEnemy->position.h][theEnemy->position.v] = theEnemy->standingOnEntityItem;
theEnemy->standingOnEntityItem = nil;
}
else
entityArray[theEnemy->position.h][theEnemy->position.v] = nil;
entityArray[newh][newv] = theEnemy;
DrawTile(newh, newv);
DrawTile(theEnemy->position.h, theEnemy->position.v);
theEnemy->position.h = newh;
theEnemy->position.v = newv;
}
else
{
if(theEnemy->standingOnEntityItem)
{
entityArray[theEnemy->position.h][theEnemy->position.v] = theEnemy->standingOnEntityItem;
theEnemy->standingOnEntityItem = nil;
}
else
entityArray[theEnemy->position.h][theEnemy->position.v] = nil;
theEnemy->standingOnEntityItem = entityArray[newh][newv];
entityArray[newh][newv] = theEnemy;
DrawTile(newh, newv);
DrawTile(theEnemy->position.h, theEnemy->position.v);
theEnemy->position.h = newh;
theEnemy->position.v = newv;
}
break;
case wall:
// case enemy:
// case gold:
// case exitPos:
// case tempEnemy:
break; /*Don't move to any of these!*/
}; /*case*/
} /*MoveEnemy*/
/* Initialize - create window, load graphics */
static void InitDungeon()
{
Rect windowRectangle;
Rect scrollRect;
/*Set up the window*/
SetRect(&windowRectangle, 30, 30, 250, 200);
invWindow = NewCWindow(nil, &windowRectangle, "\pInventory", true, documentProc, (WindowPtr)-1L, false, 0);
SetRect(&windowRectangle, 0, 40, 15 + kWindowSizeH * kTileSizeH, 55 + kWindowSizeV * kTileSizeV + kScoreFieldHeight);
myWindow = NewCWindow(nil, &windowRectangle, "\pDrives", true, documentProc, (WindowPtr)-1L, false, 0);
SetPort(myWindow);
gGameRect = myWindow->portRect;
gGameRect.bottom -= 15;
gGameRect.right -= 15;
scrollRect = myWindow->portRect;
scrollRect.left = scrollRect.right - 15;
scrollRect.right ++;
scrollRect.bottom -= 14;
scrollRect.top --;
scrollV = NewControl(myWindow,&scrollRect,"\p",true,0,0,kArraySizeV - kWindowSizeV,16,0);
scrollRect = myWindow->portRect;
scrollRect.top = scrollRect.bottom - 15;
scrollRect.bottom ++;
scrollRect.right -= 14;
scrollRect.left --;
scrollH = NewControl(myWindow,&scrollRect,"\p",true,0,0,kArraySizeH - kWindowSizeH,16,0);
qd.randSeed = TickCount (); /*Seed the random number generator*/
/*Load all pictures*/
floorTile = GetPicture(128); /*PICT resource #128.*/
playerTile = GetPicture(129); /*PICT resource #129.*/
enemyTile = GetPicture(130); /*PICT resource #130.*/
goldTile = GetPicture(131); /*PICT resource #131.*/
wallTile = GetPicture(132); /*PICT resource #132.*/
exitTile = GetPicture(133); /*PICT resource #133.*/
fileTile = GetPicture(135);
folderTile = GetPicture(134);
appTile = GetPicture(136);
deadPlayer = GetPicture(137);
} /*InitDungeon*/
/* Set up for a new game */
static void NewGame()
{
/* Fill in the tileArr array */
CreateLevel();
/* Start with a healthy player */
playerHitPoints = 10;
gGold = 0;
gExperience = 0;
} /*NewGame*/
/* ValidMove checks if a tile clickedTile is inside the array bounds *and* near the player */
static Boolean ValidMove(Point clickedTile)
{
/* Valid tile?*/
if ( clickedTile.h >= 0 )
if ( clickedTile.v >= 0 )
if ( clickedTile.h < kArraySizeH )
if ( clickedTile.v < kArraySizeV )
/* OK, we are inside the game area, clicking in some space! Is it next to the player?*/
if ( clickedTile.h >= player->position.h - 1 )
if ( clickedTile.h <= player->position.h + 1 )
if ( clickedTile.v >= player->position.v - 1 )
if ( clickedTile.v <= player->position.v + 1 )
return true;
/* If that wasn't the case, it wasn't vcalid; return false! */
return false;
} /*ValidMove*/
/* Try to move the player to the position where we clicked. */
static void MovePlayer(Point clickedTile)
{
short h, v;
GameEntityPtr theEntity;
Boolean freedOldPlayerSpot = false;
/* Valid move?*/
if (ValidMove(clickedTile))
/* Yes! What is there? */
{
ScrollIntoView(clickedTile);
if (entityArray[clickedTile.h][clickedTile.v] != nil)
{
switch (entityArray[clickedTile.h][clickedTile.v]->kind)
{
case enemyEntity:
if ( Rand(10) > 4 )
{ /*Hit! Play a "monster died" sound*/
entityArray[clickedTile.h][clickedTile.v]->value--;
if (entityArray[clickedTile.h][clickedTile.v]->value <= 0)
{ /* Monster died! */
GameEntityPtr monster;
PlaySoundAsync("\pEnemy died");
/* Walk to the space where the monster was.*/
/*tileArray[player->position.h][player->position.v] = empty;
tileArray[clickedTile.h][clickedTile.v] = player;*/
monster = entityArray[clickedTile.h][clickedTile.v];
if(monster->standingOnEntityItem)
entityArray[clickedTile.h][clickedTile.v] = monster->standingOnEntityItem;
KillEntity(monster);
if(player->standingOnEntityItem && !freedOldPlayerSpot)
{
entityArray[player->position.h][player->position.v] = player->standingOnEntityItem;
player->standingOnEntityItem = nil;
}
else
entityArray[player->position.h][player->position.v] = nil;
if(entityArray[clickedTile.h][clickedTile.v] != nil)
{
player->standingOnEntityItem = entityArray[clickedTile.h][clickedTile.v];
}
entityArray[clickedTile.h][clickedTile.v] = player;
DrawTile(player->position.h, player->position.v);
DrawTile(clickedTile.h, clickedTile.v);
player->position = clickedTile;
/* Killed a monster! Get experience! Let's say one point for the troll! */
AddScore(0, 1);
}
else /* Monster still lives! */
{
}
}
else
{ /*Miss! Play the "miss" sound. */
if ( noErr != SndPlay(nil, (SndListHandle)GetNamedResource('snd ', "\pEnemy miss"), false) )
;
}
break;
case goldEntity:
/* Gold - add score! */
AddScore(entityArray[clickedTile.h][clickedTile.v]->value, 0);
KillEntity(entityArray[clickedTile.h][clickedTile.v]);
if(player->standingOnEntityItem)
{
entityArray[player->position.h][player->position.v] = player->standingOnEntityItem;
player->standingOnEntityItem = nil;
}
else
entityArray[player->position.h][player->position.v] = nil;
entityArray[clickedTile.h][clickedTile.v] = player;
DrawTile(player->position.h, player->position.v);
DrawTile(clickedTile.h, clickedTile.v);
player->position = clickedTile;
PlaySoundAsync("\pMoney");
break;
case directoryEntity:
case fileEntity:
case applicationEntity:
if(player->standingOnEntityItem)
{
entityArray[player->position.h][player->position.v] = player->standingOnEntityItem;
freedOldPlayerSpot = true;
}
else
entityArray[player->position.h][player->position.v] = nil;
player->standingOnEntityItem = entityArray[clickedTile.h][clickedTile.v];
entityArray[clickedTile.h][clickedTile.v] = player;
DrawTile(player->position.h, player->position.v);
DrawTile(clickedTile.h, clickedTile.v);
DrawScore();
player->position = clickedTile;
break;
} /* case kind */
}
else
switch ( tileArray[clickedTile.h][clickedTile.v] )
{
case empty:
case app:
case file:
case folder:
case fakeWall:
if(tileArray[clickedTile.h][clickedTile.v] == fakeWall)
tileArray[clickedTile.h][clickedTile.v] = empty;
if(player->standingOnEntityItem && !freedOldPlayerSpot)
{
entityArray[player->position.h][player->position.v] = player->standingOnEntityItem;
player->standingOnEntityItem = nil;
}
else
entityArray[player->position.h][player->position.v] = nil;
if(entityArray[clickedTile.h][clickedTile.v] != nil)
{
player->standingOnEntityItem = entityArray[clickedTile.h][clickedTile.v];
}
entityArray[clickedTile.h][clickedTile.v] = player;
DrawTile(player->position.h, player->position.v);
DrawTile(clickedTile.h, clickedTile.v);
player->position = clickedTile;
DrawScore();
break;
case wall:
SysBeep(1);
break;
case exitPos:
if(player->standingOnEntityItem && !freedOldPlayerSpot)
{
entityArray[player->position.h][player->position.v] = player->standingOnEntityItem;
player->standingOnEntityItem = nil;
}
else
entityArray[player->position.h][player->position.v] = nil;
entityArray[clickedTile.h][clickedTile.v] = player;
DrawTile(player->position.h, player->position.v);
DrawTile(clickedTile.h, clickedTile.v);
player->position = clickedTile;
if ( noErr != SndPlay(nil, (SndListHandle)GetNamedResource('snd ', "\pNext level"), false) )
;
{
long newParId;
if(GetParentID(levelVRefNum,levelParId,"\p",&newParId) == noErr)
{
levelParId = newParId;
}
}
CreateLevel(); /* Don't quit - make a new level instead */
break;
}; /*case*/
}; /*valid move*/
ShowAroundPlayer();
/* Monsters are allowed to move now!*/
theEntity = gEntityList;
while (theEntity != nil)
{
if (theEntity->kind == enemyEntity)
MoveEnemy(theEntity);
theEntity = theEntity->next;
}
} /*MovePlayer*/
/*
* ScrollProc is the function used in a call to TrackControl for
* scrolling a document window.
*/
static pascal void ScrollProc(ControlHandle theControl, short theCode)
{ long delta = 0, pageDelta, offset, newLine, realDelta;
if (theCode == 0)
return;
pageDelta = 5;
switch(theCode) {
case kControlUpButtonPart:
delta = -1;
break;
case kControlDownButtonPart:
delta = 1;
break;
case kControlPageUpPart:
delta = -pageDelta;
break;
case kControlPageDownPart:
delta = pageDelta;
break;
}
realDelta = GetControlValue(theControl);
SetControlValue(theControl, GetControlValue(theControl) + delta);
realDelta = GetControlValue(theControl) - realDelta; //readjust in case we hit max or min
{
Rect clip;
RgnHandle saveClip;
RgnHandle invalRgn;
SetPort(myWindow);
saveClip = NewRgn();
invalRgn = NewRgn();
GetClip(saveClip);
clip = gGameRect;
clip.bottom -= kScoreFieldHeight;
ClipRect(&clip);
if(theControl == scrollV)
ScrollRect(&clip,0,-realDelta * kTileSizeV,invalRgn);
else
ScrollRect(&clip,-realDelta * kTileSizeH,0,invalRgn);
InvalRgn(invalRgn);
SetClip(saveClip);
DisposeRgn(saveClip);
DisposeRgn(invalRgn);
DoUpdate();
}
}
/* Handle mouse downs */
static void DoMouse(Point clickPoint, long mods)
{
Point clickedTile;
short h, v, part;
ControlHandle ctl;
SetPort(myWindow);
/*Convert clickPoint to local coordinates*/
GlobalToLocal(&clickPoint);
part = FindControl(clickPoint,myWindow,&ctl);
if(part && ctl)
{
short value, scrollAmt =0;
if(part == kControlIndicatorPart)
{
value = GetControlValue(ctl);
part = TrackControl(ctl,clickPoint,nil);
if(part)
{
scrollAmt = GetControlValue(ctl) - value;
}
}
else
{
ControlActionUPP controlAction;
controlAction = NewControlActionProc(ScrollProc);
TrackControl(ctl, clickPoint, controlAction);
DisposeRoutineDescriptor(controlAction);
}
/* if(scrollAmt)
{
Rect clip;
RgnHandle saveClip;
RgnHandle invalRgn;
SetControlValue(ctl, GetControlValue(ctl) + scrollAmt);
saveClip = NewRgn();
invalRgn = NewRgn();
GetClip(saveClip);
clip = gGameRect;
clip.bottom -= kScoreFieldHeight;
ClipRect(&clip);
if(ctl == scrollV)
ScrollRect(&clip,0,32 * kTileSizeV,invalRgn);
else
ScrollRect(&clip,32 * kTileSizeH,0,invalRgn);
InvalRgn(invalRgn);
SetClip(saveClip);
DisposeRgn(saveClip);
DisposeRgn(invalRgn);
}
*/ }
else
{
/* If the hero is dead, we can't move! */
if ( playerHitPoints < 1 )
{
SysBeep(1);
return;
};
/*Convert it to coordinates in the arrays.*/
clickedTile.h = clickPoint.h / kTileSizeH;
clickedTile.v = clickPoint.v / kTileSizeV;
MovePlayer(clickedTile);
}
} /*DoMouse*/
/* Empty stub for a background task. */
static void DoBackground()
{
} /*DoBackground*/
/* Add two points - simplifies the keydown handler */
static Point AddPoints(Point p1,Point p2)
{
Point dest;
dest.h = p1.h + p2.h;
dest.v = p1.v + p2.v;
return dest;
}; /*AddPoints*/
/* Handle key downs */
static void DoKey(char theKey, long mods)
{
/* If the hero is dead, we can't move! */
if ( playerHitPoints < 1 )
{
SysBeep(1);
return;
};
/* For now, we use a hard-coded key mapping. */
switch ( theKey )
{
case '^':
ShowAll();
break;
case '$':
PayForHealth();
break;
case 'd':
case '6':
MovePlayer(AddPoints(player->position, directionTable[0]));break;
case 'e':
case '9':
MovePlayer(AddPoints(player->position, directionTable[1]));break;
case 'w':
case '8':
MovePlayer(AddPoints(player->position, directionTable[2]));break;
case 'q':
case '7':
MovePlayer(AddPoints(player->position, directionTable[3]));break;
case 'a':
case '4':
MovePlayer(AddPoints(player->position, directionTable[4]));break;
case 'z':
case '1':
MovePlayer(AddPoints(player->position, directionTable[5]));break;
case 'x':
case '2':
MovePlayer(AddPoints(player->position, directionTable[6]));break;
case 'c':
case '3':
MovePlayer(AddPoints(player->position, directionTable[7]));break;
case 'o':
OpenItem(player->standingOnEntityItem);
break;
case 'g':
if(player->standingOnEntityItem)
{
PickUpItem(player->standingOnEntityItem);
}
break;
case 0x0d:
DropItems(player->standingOnEntityItem);
break;
case 0x03:
if(player->standingOnEntityItem)
{
if(player->standingOnEntityItem->kind == directoryEntity)
{
FSSpec spec;
long newDirId;
Boolean isDir;
spec = player->standingOnEntityItem->spec;
FSpGetDirectoryID(&spec, &newDirId, &isDir);
if(isDir) //sanityCheck
{
levelParId = newDirId;
levelVRefNum = spec.vRefNum;
CreateLevel(); /* Don't quit - make a new level instead */
player->standingOnEntityItem = nil;
}
}
}
break;
default:
SysBeep(1);
}; /*case*/
}; /*DoKey*/
/* Handle selections in the File menu */
static void DoFileMenu(short item)
{
switch ( item )
{
case 1:
NewGame();break;
case 3:
gDone = true;break;
}; /*case*/
}; /*DoFileMenu*/
/* The "About" item was selected */
static void DoAbout()
{
short ignore;
ignore = Alert(128, nil);
}; /*DoAbout*/
/* Handle update events */
static void DoUpdate()
{
short h, v;
SetPort(myWindow);
BeginUpdate(myWindow);
/*Draw all tiles!*/
for ( h = 0 ; h < kArraySizeH ; h++)
for ( v = 0 ; v < kArraySizeV ; v++)
DrawTile(h, v);
DrawScore();
EndUpdate(myWindow);
DrawGrowIcon(myWindow);
DrawControls(myWindow);
}; /*DoUpdate*/
static void UpdateInventory()
{
Rect rect;
HeldItemPtr items;
BeginUpdate(invWindow);
SetPort(invWindow);
TextFont(applFont);
TextSize(12);
rect.top = 5;
rect.left = 5;
rect.bottom = rect.top + 32;
rect.right = rect.left + 32;
items = heldItemsList;
while(items)
{
if(items->iconSuite)
PlotIconSuite(&rect,atVerticalCenter | atHorizontalCenter, ttNone, items->iconSuite);
MoveTo(rect.right + 10, rect.bottom - 10);
DrawString(items->spec.name);
OffsetRect(&rect, 0,34);
items = items->next;
}
EndUpdate( invWindow);
}
/* Handle menu selections */
static void DoMenuSelection(long mSelect)
{
short menuID;
short menuItem;
GrafPtr savePort;
Str255 name;
short ignore;
menuID = HiWord(mSelect);
menuItem = LoWord(mSelect);
switch ( menuID )
{
case appleID:
if ( menuItem == 1 )
DoAbout();
else
{
GetPort(&savePort);
GetMenuItemText(GetMenuHandle(appleID), menuItem, name);
ignore = OpenDeskAcc(name);
SetPort(savePort);
};
break;
case fileID:
DoFileMenu(menuItem);break;
default:
;}; /*case*/
HiliteMenu(0);
}; /*DoMenuSelection*/
/* Set up menus */
static void SetupMenus()
{
MenuHandle appleMenu, fileMenu;
Str255 tempStr;
tempStr[0]=1; tempStr[1] = (char)20;
appleMenu = NewMenu(appleID, tempStr); /*Apple menu symbol*/
InsertMenu(appleMenu, 0);
AppendMenu(appleMenu, "\pAbout Dungeon…;(-");
AppendResMenu(appleMenu, 'DRVR');
fileMenu = GetMenu(128);
InsertMenu(fileMenu, 0);
DrawMenuBar ();
}; /*SetupMenus*/
/* Main event loop */
static void MainLoop(void)
{
#define kSleep 5 /*Real programs may modify the sleep time depending on whether or not they are in the front*/
EventRecord theEvent;
char theKey;
long whatSelection;
short whichPart;
WindowPtr whichWindow;
Rect r;
Point diskInitPt = {40,40};
/*Get the next event with WaitNextEvent.*/
if ( WaitNextEvent(everyEvent, &theEvent, kSleep, nil) )
switch ( theEvent.what )
{
case mouseDown:
/*We must find out what kind of mouse down this was.*/
whichPart = FindWindow(theEvent.where, &whichWindow);
switch ( whichPart )
{
case inMenuBar:
/*Click in menu bar. Let the system call MenuSelect track it.*/
whatSelection = MenuSelect(theEvent.where);
DoMenuSelection(whatSelection); /*Our own routine for handling menu selections*/
break;
case inSysWindow:
/*Click in some window that isn't ours*/
SystemClick(&theEvent, whichWindow);
break;
case inGoAway:
/*Click in close box of window. For "desk accessory"-style games, that is a good quit signal*/
if ( (TrackGoAway(whichWindow, theEvent.where)) )
gDone = true;
break;
case inDrag:
/*Drag a window*/
if ( (whichWindow != FrontWindow ()) && ((theEvent.modifiers, cmdKey) == 0) )
SelectWindow(whichWindow);
DragWindow(whichWindow, theEvent.where, &qd.screenBits.bounds);
break;
case inGrow:
; /*Ignored - we don't resize*/
break;
case inContent:
/*Click in the window.*/
if ( (whichWindow != FrontWindow ()) )
SelectWindow(whichWindow);
else
DoMouse(theEvent.where, theEvent.modifiers); /*Go to application-specific mouse down handling*/
}; /*case whichPart*/
break; /*mouseDown*/
case keyDown:
case autoKey:
if(myWindow)
SetPort(myWindow);
/*If the command key is pressed, it is a menu selection*/
theKey = (char)(theEvent.message & charCodeMask);
if ( ((theEvent.modifiers & cmdKey) != 0) )
DoMenuSelection(MenuKey(theKey)); /*Our own routine for handling menu selections*/
else
/*Otherwise, it's a normal key down.*/
DoKey(theKey, theEvent.modifiers);
break;
case updateEvt:
/*Find out what event the update event is for.*/
if ( (WindowPtr)theEvent.message == myWindow )
DoUpdate();
if( (WindowPtr)theEvent.message == invWindow)
UpdateInventory();
break;
case diskEvt:
/*Handle bad disk insertions*/
if (HiWord(theEvent.message) != noErr)
{
DILoad();
DIBadMount(diskInitPt, theEvent.message);
DIUnload();
}
break;
default: /*Other events are ignored*/
;}; /*case*/
/*For each turn to the event loop, we may to call some "background" process, e.g. animations.*/
DoBackground();
} /*MainLoop*/
/* Standard inits */
static void InitToolbox(void) {
InitGraf (&qd.thePort);
InitFonts ();
FlushEvents (everyEvent,0);
InitWindows ();
InitMenus ();
TEInit ();
InitDialogs (nil);
InitCursor ();
}
/* Main program */
void main(void)
{
InitToolbox();
InitDungeon();
SetupMenus();
NewGame();
/*Initializations done! Run the game loop until the game ends.*/
do
{
MainLoop();
} while (! gDone);
} /*Dungeon*/
OSErr ReadFSSpecIndex( // read an indexed FSSpec entry
SInt16 pVolume, // the volume
SInt32 pDirectory, // the directory
SInt32 pIndex, // the index
FSSpec *rFSSpec, // returns the FSSpec
Boolean *rEndOfCatalog) // returns end of catalog
{
OSErr rResult;
CInfoPBRec vCatInfo;
rFSSpec->vRefNum = pVolume;
rFSSpec->parID = pDirectory;
rResult = HFS_ReadCatIndex(pVolume, pDirectory, pIndex, &vCatInfo, rFSSpec->name, rEndOfCatalog);
return rResult;
}
/*
_ZeroBlock
*/
static void _ZeroBlock(Ptr pBlock, long pSize)
{
long x;
for(x=0;x<pSize;x++) *pBlock++ = 0;
}
typedef union tHFS_PBRecTag
{
CInfoPBRec cInfo;
HFileInfo hFileInfo;
DirInfo dirInfo;
HParamBlockRec hParamBlock;
HIOParam ioParam;
HFileParam fileParam;
HVolumeParam volumeParam;
AccessParam accessParam;
ObjParam objParam;
CopyParam copyParam;
WDParam wdParam;
FIDParam fidParam;
CSParam csParam;
ForeignPrivParam foreignPrivParam;
ParamBlockRec paramBlock;
}
tHFS_PBRec;
// HFS parameter block for general i/o calls
typedef struct tHFS_ParamBlockTag
{
struct tHFS_ParamBlockTag *fNext; // next param block in list
tHFS_PBRec fParamBlock; // param block union used by HFS calls
Str63 fName; // name
UInt32 fRequestTime; // request time
SInt16 fAccessRefNum; // access ref num if used in open fork
}
tHFS_ParamBlock;
OSErr HFS_ReadCatIndex( // read an indexed catalog entry
SInt16 pVolume, // the volume
SInt32 pDirectory, // the directory
SInt32 pIndex, // the index
CInfoPBRec *rCatInfo, // returns the data
unsigned char *rName, // returns the name
Boolean *rEndOfCatalog) // returns end of catalog
{
OSErr rResult = noErr;
tHFS_ParamBlock vParamBlock;
// get a parameter block
// rResult = HFS_GetParamBlock(NULL, &vParamBlock);
_ZeroBlock((Ptr)&vParamBlock, sizeof(tHFS_ParamBlock));
vParamBlock.fParamBlock.ioParam.ioNamePtr = vParamBlock.fName;
// if this is the system root, get the info for the volume
if (!rResult && pVolume == 0)
{
vParamBlock.fParamBlock.volumeParam.ioVRefNum = 0;
vParamBlock.fParamBlock.volumeParam.ioVolIndex = pIndex;
// initiate request to get the volume reference number
rResult = PBHGetVInfoSync(&vParamBlock.fParamBlock.hParamBlock);
// save the results and get another parameter block
if (!rResult)
{
pVolume = vParamBlock.fParamBlock.volumeParam.ioVRefNum;
pDirectory = 2;
pIndex = -1;
// rResult = HFS_GetParamBlock(NULL, &vParamBlock);
_ZeroBlock((Ptr)&vParamBlock, sizeof(tHFS_ParamBlock));
vParamBlock.fParamBlock.ioParam.ioNamePtr = vParamBlock.fName;
}
}
// get the info for the directory
if (!rResult)
{
vParamBlock.fParamBlock.dirInfo.ioVRefNum = pVolume;
vParamBlock.fParamBlock.dirInfo.ioFDirIndex = pIndex;
vParamBlock.fParamBlock.dirInfo.ioDrDirID = pDirectory;
// initiate catalog info request
rResult = PBGetCatInfoSync(&vParamBlock.fParamBlock.cInfo);
// return the result
if (!rResult)
{
*rCatInfo = vParamBlock.fParamBlock.cInfo;
BlockMoveData(vParamBlock.fName, rName, vParamBlock.fName[0] + 1);
*rEndOfCatalog = FALSE;
}
else // rResult
{
*rEndOfCatalog = TRUE;
rResult = noErr;
}
}
else // rResult
*rEndOfCatalog = TRUE;
return rResult;
}
static pascal OSErr GetFDFlags(short vRefNum,
long dirID,
ConstStr255Param name,
Boolean *getBits,
unsigned short flagBits)
{
CInfoPBRec pb;
Str31 tempName;
OSErr error;
short realVRefNum;
long parID;
/* Protection against File Sharing problem */
if ( (name == NULL) || (name[0] == 0) )
{
tempName[0] = 0;
pb.hFileInfo.ioNamePtr = tempName;
pb.hFileInfo.ioFDirIndex = -1; /* use ioDirID */
}
else
{
pb.hFileInfo.ioNamePtr = (StringPtr)name;
pb.hFileInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */
}
pb.hFileInfo.ioVRefNum = vRefNum;
pb.hFileInfo.ioDirID = dirID;
error = PBGetCatInfoSync(&pb);
if ( error == noErr )
{
*getBits = (pb.hFileInfo.ioFlFndrInfo.fdFlags & flagBits) != 0;
}
return ( error );
}
OSErr LaunchApplicationWithDocument(const FSSpec * applicationFSSpecPtr,
FSSpecArrayHandle fah)
{
OSErr retCode;
LaunchParamBlockRec launchParams;
ProcessSerialNumber myPSN;
AppleEvent theAppleEvent;
AEDesc myAddrDesc, launchParamDesc, docDesc;
AEDescList docDescList;
AliasHandle docAlias;
OSErr myErr;
unsigned short items, index;
AliasHandle documentAlias;
// to simplify cleanup, ensure that handles are nil to start
theAppleEvent.dataHandle = nil;
launchParamDesc.dataHandle = nil;
docDescList.dataHandle = nil;
docDesc.dataHandle = nil;
docAlias = nil;
// the Apple event will need a valid address descriptor (even though its
// contents will not matter since we will not be calling AESend) so make
// one using my own serial number
(void) GetCurrentProcess(&myPSN);
retCode = AECreateDesc(typeProcessSerialNumber, (Ptr) &myPSN,
sizeof(ProcessSerialNumber), &myAddrDesc);
if (retCode != noErr) goto Bail;
// make a descriptor list containing just a descriptor with an
// alias to the document
retCode = AECreateList(nil, 0, false, &docDescList);
if (retCode != noErr) goto Bail;
convertfahtoAElist(fah, &docDescList);
// now make the 'odoc' AppleEvent descriptor and insert the
// document descriptor list as the direct object
retCode = AECreateAppleEvent(kCoreEventClass, kAEOpenDocuments,
&myAddrDesc, kAutoGenerateReturnID, kAnyTransactionID,
&theAppleEvent);
if (retCode != noErr) goto Bail;
retCode = AEPutParamDesc(&theAppleEvent, keyDirectObject, &docDescList);
if (retCode != noErr) goto Bail;
// this Apple event will not be sent but rather will be used
// as a parameter to the LaunchApplication call, so coerce it
// to the magic type typeAppParamters
retCode = AECoerceDesc(&theAppleEvent, typeAppParameters, &launchParamDesc);
if (retCode != noErr) goto Bail;
// finally, fill in the launch parameter block, including the
// Apple event, and make the launch call
HLock((Handle) launchParamDesc.dataHandle);
launchParams.launchAppParameters =
(AppParametersPtr) *(launchParamDesc.dataHandle);
launchParams.launchBlockID = extendedBlock;
launchParams.launchEPBLength = extendedBlockLen;
launchParams.launchFileFlags = launchNoFileFlags;
launchParams.launchControlFlags = launchContinue;
launchParams.launchAppSpec = (FSSpecPtr) applicationFSSpecPtr;
{
FInfo fndrInfo;
FSSpec appFile;
Boolean wasChanged;
FSpGetFInfo(applicationFSSpecPtr, &fndrInfo);
}
retCode = LaunchApplication(&launchParams);
if(retCode == -108 && launchParams.launchMinimumSize < launchParams.launchAvailableSize) {
launchParams.launchControlFlags = launchContinue | launchUseMinimum;
retCode = LaunchApplication(&launchParams);
}
Bail:
// dispose of everything that was allocated
if (theAppleEvent.dataHandle != nil) (void) AEDisposeDesc(&theAppleEvent);
if (launchParamDesc.dataHandle != nil) (void) AEDisposeDesc(&launchParamDesc);
if (docDescList.dataHandle != nil) (void) AEDisposeDesc(&docDescList);
if (docDesc.dataHandle != nil) (void) AEDisposeDesc(&docDesc);
if (launchParamDesc.dataHandle != nil) (void) AEDisposeDesc(&launchParamDesc);
if (docAlias != nil)
DisposeHandle((Handle) docAlias);
return retCode;
}
OSErr FindAProcess(OSType typeToFind, OSType creatorToFind, ProcessSerialNumberPtr processSN,ProcessInfoRecPtr infoRecToFill)
{
ProcessInfoRec tempInfo;
FSSpec procSpec;
Str31 processName;
OSErr myErr = noErr;
processSN->lowLongOfPSN = kNoProcess;
processSN->highLongOfPSN = kNoProcess;
tempInfo.processInfoLength = sizeof(ProcessInfoRec);
tempInfo.processName = (StringPtr)&processName;
tempInfo.processAppSpec = &procSpec;
while ((tempInfo.processSignature != creatorToFind || tempInfo.processType != typeToFind ||
myErr != noErr))
{
myErr = GetNextProcess(processSN);
if (myErr == noErr) {
GetProcessInformation(processSN, &tempInfo);
}
else {
break;
}
}
if(infoRecToFill)
*infoRecToFill = tempInfo;
return myErr;
}
static Boolean GetIndVRefNum(short which, short *vRefNum, StringPtr name)
{
OSErr theErr = noErr;
short index;
VolumeParam vpb;
Str255 volName;
vpb.ioCompletion = nil;
for(index = 1; theErr == noErr; index ++) {
vpb.ioVolIndex = index;
vpb.ioNamePtr = name;
vpb.ioVRefNum = 0;
theErr = PBGetVInfoSync((ParmBlkPtr)&vpb);
if( (index == which) && (theErr == noErr) ) {
*vRefNum = vpb.ioVRefNum;
return true;
}
}
return false;
}
/**************************************************************************
*
* Procedure PlaySoundAsync
*
* If preferences allow, play the click sound asynchronously.
* //sound.h
**************************************************************************/
Handle gSound = nil;
SndChannelPtr gChannel = nil;
SndCallBackUPP soundCallbackProc;
static void SoundCallback(SndChannelPtr chan, SndCommand *cmd)
{
// dprintf("Channel %08lx, cmd->cmd = %d, param1 = %d, param2 = %08lx", chan, cmd->cmd, cmd->param1, cmd->param2);
}
void PlaySoundAsync(StringPtr soundName)
{
Handle sndHandle;
OSErr theErr;
if(!soundCallbackProc)
soundCallbackProc = NewSndCallBackProc(SoundCallback);
if(gChannel) SndDisposeChannel(gChannel, TRUE);
gSound = GetNamedResource('snd ', soundName);
if(gSound) {
gChannel = nil;
theErr = SndNewChannel(&gChannel, 5, initMono, soundCallbackProc);
theErr = SndPlay( gChannel, (SndListHandle)gSound, TRUE );
}
}
static void SendODOCToProcess(ProcessSerialNumberPtr psn, FSSpecArrayHandle fah)
{
short errnum;
AEDesc targetaddress;
AEDescList selectionlist;
AppleEvent myae, myreply;
if (((errnum = AECreateDesc(typeProcessSerialNumber, psn, sizeof(ProcessSerialNumber), &targetaddress)) == noErr) &&
((errnum = AECreateAppleEvent(kCoreEventClass, kAEOpenDocuments, &targetaddress, kAutoGenerateReturnID, kAnyTransactionID, &myae)) == noErr) &&
((errnum = AEDisposeDesc(&targetaddress)) == noErr) &&
((errnum = AECreateList(nil, 0, FALSE, &selectionlist)) == noErr) &&
((errnum = convertfahtoAElist(fah, &selectionlist)) == noErr) &&
((errnum = AEPutParamDesc(&myae, keyDirectObject, &selectionlist)) == noErr) &&
((errnum = AEDisposeDesc(&selectionlist)) == noErr))
{
if ((errnum == noErr) &&
((errnum = AESend(&myae, &myreply, kAENoReply, kAENeverInteract, kAEDefaultTimeout, nil, nil)) == noErr) &&
((errnum = AEDisposeDesc(&myreply)) == noErr))
errnum = AEDisposeDesc(&myae);
}
}
/*-------------*
| IsAliasFile |
*-------------*/
static pascal OSErr IsAliasFile(const FSSpec *fileFSSpec,
Boolean *aliasFileFlag,
Boolean *folderFlag)
/* sets aliasFileFlag if the FSSpec points to an alias file;
sets folderFlag if the FSSpec points to a folder */
{
CInfoPBRec myCInfoPBRec;
OSErr retCode;
if (fileFSSpec == nil || aliasFileFlag == nil || folderFlag == nil)
return paramErr;
*aliasFileFlag = *folderFlag = false;
/* get the item's catalog information */
myCInfoPBRec.hFileInfo.ioCompletion = nil;
myCInfoPBRec.hFileInfo.ioNamePtr = (StringPtr)&fileFSSpec->name;
myCInfoPBRec.hFileInfo.ioVRefNum = fileFSSpec->vRefNum;
myCInfoPBRec.hFileInfo.ioDirID = fileFSSpec->parID;
myCInfoPBRec.hFileInfo.ioFVersNum = 0; /* MFS compatibility, see TN #204 */
myCInfoPBRec.hFileInfo.ioFDirIndex = 0;
retCode = PBGetCatInfoSync(&myCInfoPBRec);
/* set aliasFileFlag if the item is not a directory and the
aliasFile bit is set */
if (retCode == noErr) {
/* check directory bit */
if ((myCInfoPBRec.hFileInfo.ioFlAttrib & ioDirMask) != 0)
*folderFlag = true;
/* check isAlias bit */
else if ((myCInfoPBRec.hFileInfo.ioFlFndrInfo.fdFlags & 0x8000) != 0)
*aliasFileFlag = true;
}
return retCode;
}
/*-----------------------------*
| ResolveAliasFileMountOption |
*-----------------------------*/
pascal OSErr ResolveAliasFileMountOption(FSSpec *fileFSSpec,
Boolean resolveAliasChains,
Boolean *targetIsFolder,
Boolean *wasAliased,
Boolean mountRemoteVols)
{
/* maximum number of aliases to resolve before giving up */
#define MAXCHAINS 10
short myResRefNum;
Handle alisHandle;
FSSpec initFSSpec;
Boolean updateFlag, foundFlag, wasAliasedTemp, specChangedFlag;
short chainCount;
OSErr retCode;
if (fileFSSpec == nil || targetIsFolder == nil || wasAliased == nil)
return paramErr;
initFSSpec = *fileFSSpec; /* so FSSpec can be restored in case of error */
chainCount = MAXCHAINS; /* circular alias chain protection */
myResRefNum = -1; /* resource file not open */
*targetIsFolder = foundFlag = specChangedFlag = false;
/* loop through chain of alias files */
do {
chainCount--;
/* check if FSSpec is an alias file or a directory */
/* note that targetIsFolder => NOT wasAliased */
retCode = IsAliasFile(fileFSSpec, wasAliased, targetIsFolder);
if (retCode != noErr || !(*wasAliased)) break;
/* get the resource file reference number */
myResRefNum = FSpOpenResFile(fileFSSpec, fsCurPerm);
retCode = ResError();
if (myResRefNum == -1) break;
/* the first 'alis' resource in the file is the appropriate alias */
alisHandle = Get1IndResource(rAliasType, 1);
retCode = ResError();
if (alisHandle == nil) break;
/* load the resource explicitly in case SetResLoad(FALSE) */
LoadResource(alisHandle);
retCode = ResError();
if (retCode != noErr) break;
retCode = FollowFinderAlias(fileFSSpec, (AliasHandle) alisHandle,
mountRemoteVols, fileFSSpec, &updateFlag);
/* FollowFinderAlias returns nsvErr if volume not mounted */
if (retCode == noErr) {
if (updateFlag) {
/* the resource in the alias file needs updating */
ChangedResource(alisHandle);
WriteResource(alisHandle);
}
specChangedFlag = true; /* in case of error, restore file spec */
retCode = IsAliasFile(fileFSSpec, &wasAliasedTemp, targetIsFolder);
if (retCode == noErr)
/* we're done unless it was an alias file and we're following a chain */
foundFlag = !(wasAliasedTemp && resolveAliasChains);
}
CloseResFile(myResRefNum);
myResRefNum = -1;
} while (retCode == noErr && chainCount > 0 && !foundFlag);
/* return file not found error for circular alias chains */
if (chainCount == 0 && !foundFlag) retCode = fnfErr;
/* if error occurred, close resource file and restore the original FSSpec */
if (myResRefNum != -1) CloseResFile(myResRefNum);
if (retCode != noErr && specChangedFlag) *fileFSSpec = initFSSpec;
return retCode;
}
/*What's left for making a real game of it?*/
/*- Animations*/
/*- Several levels*/
/*- Faster drawing*/
/*- Asynch sound*/
/*- More objects, i.e. weapons, monsters, treasures…*/